package com.bibimaya.auto.mapper.dao.mapper;

import com.bibimaya.auto.mapper.dao.annotation.AMapper;
import com.bibimaya.auto.mapper.dao.entity.BaseEntity;
import com.bibimaya.auto.mapper.dao.mapper.processor.MapperProcessor;
import com.bibimaya.auto.mapper.dao.mapper.processor.MapperProcessorFactory;
import com.bibimaya.auto.mapper.dao.utils.EntityMap;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

/**
 * Created by lijianwei
 */
public class AutoMapperAware implements ApplicationContextAware {

    private List<String> mapperList;

    private String defaultMapperSuffix;

    private String defaultEntitySuffix;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, SqlSessionFactory> sqlSessionFactories = applicationContext.getBeansOfType(SqlSessionFactory.class);
        if (sqlSessionFactories.isEmpty()) {
            return;
        }
        if(mapperList != null && mapperList.size() > 0){
            for (SqlSessionFactory sqlSessionFactory : sqlSessionFactories.values()) {
                Configuration config = sqlSessionFactory.getConfiguration();
                for(String mapperName : mapperList) {
                    try {
                        autoMapper(config, mapperName);
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                        throw new RuntimeException(e.getMessage());
                    }
                }
            }
        }
    }

    public void autoMapper(final Configuration config, final String mapperName)
            throws ClassNotFoundException {
        if(config == null || mapperName == null){
            return ;
        }

        Class mapperClass = Class.forName(mapperName);
        if(!BaseMethodMapper.class.isAssignableFrom(mapperClass)){
            return;
        }
        String entitySuffix = defaultEntitySuffix;
        Class entityClass = null;
        AMapper aMapper = (AMapper) mapperClass.getDeclaredAnnotation(AMapper.class);
        if(aMapper != null && aMapper.entityClass() != AMapper.class){
            entityClass = aMapper.entityClass();
            entitySuffix = aMapper.entitySuffix();
        }

        Type[] types = mapperClass.getGenericInterfaces();
        if(types == null || types.length == 0){
            return;
        }
        for(Type t : types){
            if(t instanceof ParameterizedType){
                Type[] typeArguments = ((ParameterizedType)t).getActualTypeArguments();
                if(typeArguments != null && typeArguments.length > 0){
                    entityClass = (Class) typeArguments[0];
                }
                Class methodInterface = (Class) ((ParameterizedType) t).getRawType();
                if(methodInterface == AllMapper.class){
                    Class<?>[] mcs = methodInterface.getInterfaces();
                    for(Class mc : mcs){
                        deal(config, mapperClass, mc, entityClass, entitySuffix);
                    }
                }else{
                    deal(config, mapperClass, methodInterface, entityClass, entitySuffix);
                }
            }
        }
    }

    private void deal(Configuration config, Class mapperClass,Class methodInterface, Class entityClass, String entitySuffix){
        if(!BaseEntity.class.isAssignableFrom(entityClass)){
            return ;
        }
        EntityMap entityMap = EntityMap.newInstance(entityClass, entitySuffix);
        MapperProcessor processor = MapperProcessorFactory.getMethodProcessor(methodInterface);
        if(processor != null){
            processor.init(config, mapperClass, entityMap);
            processor.make();
        }
    }

    public List<String> getMapperList() {
        return mapperList;
    }

    public void setMapperList(List<String> mapperList) {
        this.mapperList = mapperList;
    }

    public String getDefaultMapperSuffix() {
        return defaultMapperSuffix;
    }

    public void setDefaultMapperSuffix(String defaultMapperSuffix) {
        this.defaultMapperSuffix = defaultMapperSuffix;
    }

    public String getDefaultEntitySuffix() {
        return defaultEntitySuffix;
    }

    public void setDefaultEntitySuffix(String defaultEntitySuffix) {
        this.defaultEntitySuffix = defaultEntitySuffix;
    }
}
